\documentclass[12pt]{article}
\usepackage{xeCJK}
\usepackage[top=1in,bottom=1in,left=1.25in,right=1.25in]{geometry}
\usepackage{listings}
\usepackage{xcolor}
\lstset{
  numbers = left,  % line number
  framexleftmargin = 10mm,
  frame = none,
  backgroundcolor = \color[RGB]{245,245,244},
  keywordstyle = \bf\color{blue},
  identifierstyle = \bf,
  numberstyle = \color[RGB]{0,192,192},
  commentstyle = \it\color[RGB]{0,96,96},
  stringstyle = \rmfamily\slshape\color[RGB]{128,0,0},
  showstringspaces = false
}

\title{Ucore Source Analysis}
\setCJKmainfont{SimSun}

\begin{document}
\maketitle

\section{页表}

\subsection{页操作对应的函数及其分析}

\subsubsection{新建（NEW）}
对于页表而言，新建一个页实际上指的是修改对应表项的页帧号（实现中还要考虑页表项不存在时创建之）。ucore中通过定位参数\verb|create|为1的\verb|get_pte|调用并对其返回值进行赋值的位置，即可确定新建页表项的函数，这些函数包括：

\begin{enumerate}
\item \verb|boot_map_segment|
\item \verb|page_insert|
\item \verb|copy_range|
\item \verb|do_pgfault|
\end{enumerate}

\verb|boot_map_segment|仅用于系统刚启动时的内存重映射，暂不予考虑。

\verb|page_insert|是启动后向页表中插入页表项的主要函数，页表项属性中\verb|PTE_P|必然被设置，而\verb|PTE_U|与\verb|PTE_W|由传入的参数\verb|perm|决定（理论上\verb|perm|有可能包括\verb|PTE_A|等其它内容，但ucore中除了check以外没有这样使用的例子），查阅所有可能传入的\verb|perm|可以认为，\verb|perm|的值一般有\verb|PTE_U|（\verb|copy_range|与复制源有关，而由于复制源进程存在mm结构，必然为用户态进程，亦应有\verb|PTE_U|），而该页在vmm中是否有写属性决定了\verb|PTE_W|的值。

\verb|copy_range|完成内存管理结构复制时页表项的拷贝，参见\ref{page_copy}

\verb|do_pgfault|完成缺页异常的处理。对于因为尚未分配页而引起的异常，通过分配页或写入共享内存地址解决；若为写COW页，则分配新页、复制页内容并将当前页表项的物理地址设为新分配的页。

\subsubsection{读（READ）}
无论在内核态还是用户态，取指定地址的值即为一次读操作，只要该页在页表中存在映射即可（用户态下还需要\verb|PTE_U|），操作后\verb|PTE_A|设置为1。

\subsubsection{写（WRITE）}
无论在内核态还是用户态，向指定地址写入数据即为一次写操作，只有该页在页表中存在映射且\verb|PTE_W|置位时才能进行（用户态下还需要\verb|PTE_U|），操作后\verb|PTE_A|、\verb|PTE_D|均设置为1。

\subsubsection{加载（LOAD）}
页的加载仅在\verb|load_icode|中进行。\verb|load_icode|从文件系统读取了elf文件头之后需要在用户进程的地址空间中分配相应的页，并将elf文件的内容复制到这些页上。加载时在页表项中\verb|PTE_P|、\verb|PTE_U|总被置位，而\verb|PTE_W|只在elf头声明该段可写时被置位，其余的位为零。

\subsubsection{换出（SWAP OUT）}
页的换出由\verb|kswapd|内核线程负责，每次需要\verb|kswapd|换出一定量的页面时，\verb|kswapd|先尝试逐次遍历各个内存管理结构，将可换出的页的页表项设为已换出的形式（\verb|PTE_P|为0，前24位作为swap分区中该页的偏移量），并将相应的页加入换页链表；然后逐次访问换页链表中的页并对可换出的页执行换页操作。以上两步不修改ucore的虚存管理结构对该页的访问属性，同时只有第一步对页表项有所修改。

第一步的主要工作由\verb|swap_out_vma|完成，该函数从指定的地址开始逐次检查同一个vmm范围的所有页，若某页的页表项的\verb|PTE_P|为1，则执行下述步骤（只叙述与页表项有关的部分）：

\begin{enumerate}
\item 若\verb|PTE_A|为1，重置之为0，检查下一页；
\item 若\verb|PTE_D|为1，设置页结构的\verb|PG_DIRTY|为1；
\item 执行页表上的换页操作，即将页表项换为已换出形式。
\end{enumerate}

\subsubsection{\label{page_copy}复制（COPY）}

页表的复制由\verb|copy_range|完成，对于源页未被换出的情形，同时设置源页表项和目的页表项的\verb|PTE_W|为0（为了完成COW）并将\verb|PTE_D|和\verb|PTE_A|清零\footnote{如果写一个已被换出的页后fork一个进程，然后该页又被换出，是否可能丢失写入的数据？}；对于源页在swap分区的情形，直接赋值swap信息。

\end{document}